package com.aac.rabbitrfc.type;

import com.gzsolartech.smartforms.hiextype.OracleNativeExtractor;
import oracle.xdb.XMLType;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 此类被DatDocument的document字段的@Type注解引用
 * 原本@Type引用com.gzsolartech.smartforms.hiextype.OracleXmlType，但该类实现的UserType接口来自Hibernate4，
 * 其nullSafeGet和nullSafeSet方法的第三个参数与Hibernate5不同：Hibernate4为SessionImplementor，
 * Hibernate5为SharedSessionContractImplementor，Postman访问时会报AbstractMethodError:nullSafeGet/nullSafeSet。
 * 所以，解决方法就是重写OracleXmlType类，实现Hibernate5的UserType接口。
 */
public class OracleXmlType implements UserType, Serializable {
    private static final long serialVersionUID = 2308230823023L;
    private static final Class returnedClass = Document.class;
    private static final int[] SQL_TYPES = new int[]{2007};
    private static final Logger LOG = LoggerFactory.getLogger(OracleXmlType.class);

    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    public Class returnedClass() {
        return returnedClass;
    }

    public int hashCode(Object _obj) {
        return _obj.hashCode();
    }

    public Object assemble(Serializable _cached, Object _owner) throws HibernateException {
        try {
            return stringToDom((String)_cached);
        } catch (Exception var4) {
            throw new HibernateException("Could not assemble String to Document", var4);
        }
    }

    public Serializable disassemble(Object _obj) throws HibernateException {
        try {
            return domToString((Document)_obj);
        } catch (Exception var3) {
            throw new HibernateException("Could not disassemble Document to Serializable", var3);
        }
    }

    public Object replace(Object _orig, Object _tar, Object _owner) {
        return this.deepCopy(_orig);
    }

    public boolean equals(Object arg0, Object arg1) throws HibernateException {
        if (arg0 == null && arg1 == null) {
            return true;
        } else {
            return arg0 == null && arg1 != null ? false : arg0.equals(arg1);
        }
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value == null ? null : (Document)((Document)value).cloneNode(true);
    }

    public boolean isMutable() {
        return false;
    }

    protected static String domToString(Document _document) throws TransformerException {
        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        transformer.setOutputProperty("indent", "yes");
        if (_document == null) {
            return "<root></root>";
        } else {
            DOMSource source = new DOMSource(_document);
            StringWriter sw = new StringWriter();
            StreamResult result = new StreamResult(sw);
            transformer.transform(source, result);
            return sw.toString();
        }
    }

    protected static Document stringToDom(String xmlSource) throws SAXException, ParserConfigurationException, IOException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        if (StringUtils.isBlank(xmlSource)) {
            xmlSource = "<root></root>";
        }

        return builder.parse(new ByteArrayInputStream(xmlSource.getBytes("UTF-8")));
    }

    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor arg2, Object arg3) throws HibernateException, SQLException {
        XMLType xmlType = null;

        try {
            xmlType = (XMLType)rs.getObject(names[0]);
        } catch (Exception var7) {
            LOG.error("Hibernate get XMLType Data Error!", var7);
        }

        return xmlType != null ? xmlType.getDOM() : null;
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor arg3) throws HibernateException, SQLException {
        OracleNativeExtractor extractor = new OracleNativeExtractor();
        Connection nativeConn = extractor.getNativeConnection(st.getConnection());

        try {
            XMLType xmlType = null;
            if (value != null) {
                xmlType = new XMLType(nativeConn, domToString((Document)value));
            }

            st.setObject(index, xmlType);
        } catch (Exception var8) {
            throw new SQLException("Could not covert Document to String for storage");
        }
    }
}
